#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <assert.h>
#include <sys/stat.h>
#include <dirent.h>
#include <types.h>
#include <utils.h>
#include <limits.h>
#include <time.h>
#include <dlist.h>

uint8_t _checksum(const void *data, int len)
{
    uint8_t cs = 0;

    while (len-- > 0)
        cs += *((uint8_t *) data + len);
    return cs;
}

uint32_t get_le_val(const uint8_t *p, int bytes)
{
    uint32_t ret = 0;

    while (bytes-- > 0)
    {
        ret <<= 8;
        ret |= *(p + bytes);
    }
    return ret;
}
uint32_t get_be_val(const uint8_t *p, int bytes)
{
    uint32_t ret = 0;

    while (bytes-- > 0)
    {
        ret <<= 8;
        ret |= *p++;
    }
    return ret;
}

void put_le_val(uint32_t val, uint8_t *p, int bytes)
{
    while (bytes-- > 0)
    {
        *p++ = val & 0xFF;
        val >>= 8;
    }
}
void put_be_val(uint32_t val, uint8_t *p, int bytes)
{
    while (bytes-- > 0)
    {
        *(p + bytes) = val & 0xFF;
        val >>= 8;
    }
}

uint64_t get_le_val64(const uint8_t *p, int bytes)
{
    uint64_t ret = 0;

    while (bytes-- > 0)
    {
        ret <<= 8;
        ret |= *(p + bytes);
    }
    return ret;
}
uint64_t get_be_val64(const uint8_t *p, int bytes)
{
    uint64_t ret = 0;

    while (bytes-- > 0)
    {
        ret <<= 8;
        ret |= *p++;
    }
    return ret;
}

void put_le_val64(uint64_t val, uint8_t *p, int bytes)
{
    while (bytes-- > 0)
    {
        *p++ = val & 0xFF;
        val >>= 8;
    }
}
void put_be_val64(uint64_t val, uint8_t *p, int bytes)
{
    while (bytes-- > 0)
    {
        *(p + bytes) = val & 0xFF;
        val >>= 8;
    }
}

uint32_t get_le_bcd_val(const uint8_t *p, int bytes)
{
    uint32_t ret = 0;

    while (bytes-- > 0)
    {
        ret *= 100u;
        ret += bcd2bin(p[bytes]);
    }
    return ret;
}
uint32_t get_be_bcd_val(const uint8_t *p, int bytes)
{
    uint32_t ret = 0;

    while (bytes-- > 0)
    {
        uint8_t x = *p++;
        ret *= 100u;
        ret += bcd2bin(x);
    }
    return ret;
}

void put_le_bcd_val(uint32_t val, uint8_t *p, int bytes)
{
    while (bytes--)
    {
        uint8_t x = val % 100u;
        *p++ = bin2bcd(x);
        val /= 100u;
    }
}
void put_be_bcd_val(uint32_t val, uint8_t *p, int bytes)
{
    while (bytes--)
    {
        uint8_t x = val % 100u;
        p[bytes] = bin2bcd(x);
        val /= 100u;
    }
}

uint64_t get_le_bcd_val64(const uint8_t *p, int bytes)
{
    uint64_t ret = 0;

    while (bytes-- > 0)
    {
        ret *= 100u;
        ret += bcd2bin(p[bytes]);
    }
    return ret;
}
uint64_t get_be_bcd_val64(const uint8_t *p, int bytes)
{
    uint64_t ret = 0;

    while (bytes-- > 0)
    {
        uint8_t x = *p++;
        ret *= 100u;
        ret += bcd2bin(x);
    }
    return ret;
}

void put_le_bcd_val64(uint64_t val, uint8_t *p, int bytes)
{
    while (bytes--)
    {
        uint8_t x = val % 100u;
        *p++ = bin2bcd(x);
        val /= 100u;
    }
}
void put_be_bcd_val64(uint64_t val, uint8_t *p, int bytes)
{
    while (bytes--)
    {
        uint8_t x = val % 100u;
        p[bytes] = bin2bcd(x);
        val /= 100u;
    }
}

int is_all_xx(const void *s1, uint8_t val, int n)
{
    while (n && *(uint8_t *) s1 == val)
    {
        s1 = (uint8_t *) s1 + 1;
        n--;
    }
    return !n;
}

char *skip_spaces(const char *str)
{
    while (isspace(*str))
        ++str;
    return (char *)str;
}
char *strim(char *s)
{
    size_t size;
    char *end;

    size = strlen(s);
    if (!size)
        return s;

    end = s + size - 1;
    while (end >= s && isspace(*end))
        end--;
    *(end + 1) = '\0';

    return skip_spaces(s);
}

char *i2str(uint8_t val, char *dest)
{
    const char *charmap = "0123456789ABCDEF";

    *dest++ = charmap[get_bits(val, 4, 7)];
    *dest++ = charmap[get_bits(val, 0, 3)];
    *dest++ = '\0';
    return dest - 3;
}
const char *_arr2str(const void *arr, int len, void *dest, int maxlen, int del)
{
    const uint8_t *_arr = (const uint8_t *)arr;
    char *_dest = (char *)dest;

    while (len-- && maxlen >= 3)
    {
        i2str(*_arr++, _dest);
        _dest += 2;
        *_dest++ = del;
        maxlen -= 3;
    }
    if (_dest - (char *)dest > 0) *--_dest = '\0';

    return (const char *)dest;
}
const char *arr2str(const void *arr, int len, void *dest, int maxlen)
{
    return _arr2str(arr, len, dest, maxlen, ' ');
}

int split(char *str, char **arr, size_t narr, int del)
{
    if (!str || narr < 1) return 0;

    char **src = arr;

    for (*arr++ = str, --narr; *str && narr; ++str)
    {
        if (*str != del) continue;

        *str = '\0';
        *arr++ = str + 1;
        --narr;
    }

    return arr - src;
}

static int get_num(char c)
{
    if (c >= '0' && c <= '9')
        return c - '0';
    if (c >= 'a' && c <= 'f')
        return c - 'a' + 10;
    if (c >= 'A' && c <= 'F')
        return c - 'A' + 10;
    return -1;
}
int str2arr(const char *str, void *out, int maxlen)
{
    int i, n = 0;

    while (*str)
    {
        while (*str && !isalnum((int)*str)) str++;

        uint8_t val = 0;
        for (i = 0; i < 2 && isalnum((int)*str); i++)
        {
            val <<= 4;
            val |= get_num(*str++);
        }
        if (i)((uint8_t *)out)[n++] = val;
        if (n >= maxlen) break;
    }
    return n;
}

int startswith(const char *str, const char *pre)
{
    char cp;
    char cs;

    if (!*pre) return 1;

    while ((cp = *pre++) && (cs = *str++))
    {
        if (cp != cs)
            return 0;
    }

    return !!cs;
}
uint16_t crc16(uint16_t crc, const void *buf, int size)
{
    uint8_t i;
    uint8_t *_buf = (uint8_t *) buf;

    while (size-- != 0)
    {
        for (i = 0x80; i != 0; i >>= 1)
        {
            if ((crc & 0x8000) != 0)
            {
                crc <<= 1;
                crc ^= 0x1021;
            }
            else
            {
                crc <<= 1;
            }
            if (((*_buf) & i) != 0)
            {
                crc ^= 0x1021;
            }
        }
        _buf++;
    }
    return crc;
}

char *svc__strdup(const char *s)
{
    size_t len = strlen(s) + 1;
    char *m = kmalloc(len);
    if (m == NULL)
        return NULL;
    return memcpy(m, s, len);
}
char *svc__strndup(const char *s, size_t n)
{
    char *m;
    size_t len = strlen(s);
    if (n < len)
        len = n;
    m = kmalloc(len + 1);
    if (m == NULL)
        return NULL;
    m[len] = '\0';
    return memcpy(m, s, len);
}
ssize_t svc__strscpy(char *d, const char *s, size_t n)
{
    size_t i;

    for (i = 0; i < n; i++)
        if ('\0' == (d[i] = s[i]))
            return i > SSIZE_MAX ? -EINVAL : (ssize_t)i;

    if (i == 0)
        return 0;

    d[--i] = '\0';

    return -EINVAL;
}

#ifndef _GNU_SOURCE
void *memmem(const void *buf1, int len1, const void *buf2, int len2)
{
    int i = 0, j = 0;

    if (len2 > len1) return NULL;

    while (i < len1 && j < len2)
    {
        if (*((uint8_t *)buf1 + i) == *((uint8_t *)buf2 + j))
        {
            i++;
            j++;
        }
        else
        {
            i = i - j + 1;
            j = 0;
        }
    }
    return (j >= len2) ? ((uint8_t *)buf1 + (i - j)) : NULL;
}
#endif
void memrvs(void *buff, size_t len)
{
    uint8_t *rearp = (uint8_t *) buff + len - 1;
    uint8_t *headp = (uint8_t *) buff;

    while (headp < rearp)
    {
        swap(*headp, *rearp);
        headp++;
        rearp--;
    }
}
void memadd(const void *data, int add, size_t n)
{
    while (n--) *((u8 *)data + n) += add;
}

int membcd(const void *data, size_t n)
{
    while (n--)
    {
        uint8_t _data = *((uint8_t *)data + n);

        if ((_data & 0x0F) > 0x09 || (_data & 0xF0) > 0x90)
            return 0;
    }
    return 1;
}

int datebcd(const uint8_t *bcdtime)
{
    int i;
    const uint8_t maxvalues[] = { 0x59, 0x59, 0x23, 0x31, 0x12, 0x99 };

    if (!membcd(bcdtime, 6))
        return 0;

    for (i = 0; i < 6; i++)
        if (bcdtime[i] > maxvalues[i])
            return 0;

    if (0 == bcdtime[3] || 0 == bcdtime[4])
        return 0;
    return 1;
}

uint8_t dateweek(uint8_t y, uint8_t m, uint8_t d)
{
    const uint8_t TAB_X[12] = {6, 2, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};

    if (m < 3 && (y & 0x03) == 0)
        return (y + (y >> 2) + TAB_X[m - 1] + d - 1) % 7;

    return (y + (y >> 2) + TAB_X[m - 1] + d) % 7;
}

static const u32 _POWER10[] = {
    1, 10, 100, 1000, 10000, 100000, 1000000, 10000000,
};
static inline u64 double_round(double d, bool floor)
{
#define DOUBLE_ROUND_VAL 0.000001f

    if (!floor)
        d += 0.5;
    return (u64)(d + DOUBLE_ROUND_VAL);
}
void put_le_bcd_fval(double f, int bytes, int fbits, void *bcd, bool floor)
{
    bool sign = f < 0;

    assert(bytes > 0  && bytes <= 8);
    assert(fbits >= 0 && fbits < array_size(_POWER10));

    f = _abs(f) * (double)_POWER10[fbits];

    put_le_bcd_val64(double_round(f, floor), bcd, bytes);

    if (sign)
        set_bit(*((u8 *)bcd + bytes - 1), 7);
}
double get_le_bcd_fval(const void *bcd, int bytes, int fbits, bool withsign)
{
    int sign = 1;
    u8  buf[8];

    assert(bytes > 0  && bytes <= 8);
    assert(fbits >= 0 && fbits < array_size(_POWER10));

    memcpy(buf, bcd, bytes);

    if (withsign && tst_bit(buf[bytes-1], 7))
    {
        sign = -1;
        clr_bit(buf[bytes-1], 7);
    }

    return (double)get_le_bcd_val64(buf, bytes) * sign / (double)_POWER10[fbits];
}

unsigned int __sw_hweight8(unsigned int w)
{
    unsigned int res = w - ((w >> 1) & 0x55);
    res = (res & 0x33) + ((res >> 2) & 0x33);
    return (res + (res >> 4)) & 0x0F;
}

ev_time_t hw_time(void)
{
    struct timespec t;

    if (clock_gettime(CLOCK_MONOTONIC, &t))
        return 0;  /* Not really possible. */

    /*
     * unit: 10ms
     */
    return t.tv_sec * 100 + t.tv_nsec / 10000000;
}

const char *pr_time(const u8 *bintm)
{
    static char timestr[0x20];

    sprintf(timestr, "20%02d.%02d.%02d %02d:%02d:%02d",
            bintm[5], bintm[4], bintm[3],
            bintm[2], bintm[1], bintm[0]);
    return timestr;
}

void subject_init(struct subject_struct *sub)
{
    INIT_LIST_HEAD(&sub->head);
}
void observer_attach(struct subject_struct *sub, struct observer_struct *ob)
{
    list_add_tail(&ob->entry, &sub->head);
}
void observer_detach(struct subject_struct *sub, struct observer_struct *ob)
{
    list_del(&ob->entry);
}
void subject_notify(struct subject_struct *sub, void *arg)
{
    struct observer_struct *ob, *n;

    list_for_each_entry_safe(ob, n, &sub->head, entry)
    {
        if (ob->update)
            ob->update(ob, arg);
    }
}

char *self_path(char *path, size_t maxlen)
{
    int r = readlink("/proc/self/exe", path, maxlen - 1);
    if (r <= 0 || r >= maxlen - 1)
        return NULL;

    path[r] = '\0';
    return path;
}
static int do_mkdir(const char *path)
{
    ssize_t r;
    int tries;

    if (access(path, F_OK) == 0)
        return 0;

    tries = 3;
    do
    {
        errno = 0;
        r = mkdir(path, 0777);
    } while (r == -1 && errno == EINTR && tries-- > 0);
    return r;
}
int mkdirs(const char *muldir)
{
    int i, len, r = 0;
    char path[FILENAME_MAX];

    if (*muldir == '/')
    {
        r = chdir("/");
        if (r < 0) goto exit;
        muldir++;
    }

    svc__strscpy(path, muldir, sizeof(path));
    len = strlen(path);
    for (i = 0; i < len; i++)
    {
        if (path[i] == '/')
        {
            path[i] = '\0';
            r = do_mkdir(path);
            if (r < 0) goto exit;
            path[i] = '/';
        }
    }
    if (len > 0)
    {
        r = do_mkdir(path);
        if (r < 0) goto exit;
    }
exit:
    return r;
}

int rmdirs(const char *muldir)
{
    struct stat dstat;

    if (access(muldir, F_OK) != 0) return 0;

    if (stat(muldir, &dstat) < 0)
    {
        perror("get directory stat error");
        return -1;
    }

    if (S_ISREG(dstat.st_mode))
    {
        remove(muldir);
    }
    else if (S_ISDIR(dstat.st_mode))
    {
        char dir_name[FILENAME_MAX];
        struct dirent *dp;

        DIR *dirp = opendir(muldir);

        while ((dp = readdir(dirp)) != NULL)
        {
            if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
                continue;

            sprintf(dir_name, "%s/%s", muldir, dp->d_name);
            rmdirs(dir_name);
        }
        closedir(dirp);
        rmdir(muldir);
    }
    return 0;
}

int os_proc_pid(const char *name)
{
    DIR* dir;
    struct dirent* ent;
    char buf[512];

    long  pid;
    char pname[100] = {0,};
    char state;
    FILE *fp=NULL;

    if (!(dir = opendir("/proc")))
    {
        perror("can't open /proc");
        return -1;
    }

    while ((ent = readdir(dir)) != NULL)
    {
        long lpid = atol(ent->d_name);
        if (lpid < 0)
            continue;

        snprintf(buf, sizeof(buf), "/proc/%ld/stat", lpid);
        fp = fopen(buf, "r");

        if (fp)
        {
            if ((fscanf(fp, "%ld (%[^)]) %c", &pid, pname, &state)) != 3)
            {
                printf("fscanf failed \n");
                fclose(fp);
                closedir(dir);
                return -1;
            }
            if (!strcmp(pname, name))
            {
                fclose(fp);
                closedir(dir);
                return (int)lpid;
            }
            fclose(fp);
        }
    }

    closedir(dir);
    return -1;
}
